package net.aiion.weave.impl.painters;

import java.util.ArrayList;
import java.util.Collection;

import net.aiion.weave.spi.bytecode.Bytecode;
import net.aiion.weave.spi.injection.annotations.Inject;
import net.aiion.weave.spi.messaging.MessageCallback;
import net.aiion.weave.spi.messaging.MessageQueue;
import net.aiion.weave.spi.messaging.MessageResult;
import net.aiion.weave.spi.messaging.annotations.Queue;
import net.aiion.weave.spi.messaging.annotations.Receives;
import net.aiion.weave.spi.naming.Context;
import net.aiion.weave.spi.naming.annotations.DynamicScoped;
import net.aiion.weave.spi.naming.annotations.Named;
import net.aiion.weave.spi.painters.PaintBrush;
import net.aiion.weave.spi.painters.PaintContext;
import net.aiion.weave.spi.painters.Painter;
import net.aiion.weave.spi.painters.PainterService;
import net.aiion.weave.spi.routing.Route;
import net.aiion.weave.spi.routing.RouteMessage;

@DynamicScoped
@Named
public class PainterServiceImpl implements PainterService {
    @Inject
    private Context context;

    @Inject
    @Queue("net.aiion.weave.painters")
    private MessageQueue messageQueue;

    public PainterServiceImpl() {
    }

    @Receives("org.weaveframework.routing")
    public PaintBrush receiveRoute(final RouteMessage aRouteMessage) {
        for (String routeToken : aRouteMessage.getRoute().getTokens()) {
            final Object instance = context.lookup(routeToken);
            if (instance != null) {
                return paintComponent(instance);
            }
        }

        return EmptyPaintBrush.INSTANCE;
    }

    @Override
    public PaintBrush paintComponent(final Object aNamed) {
        if (aNamed == null) {
            return EmptyPaintBrush.INSTANCE;
        }

        final Collection<PaintBrush> paintBrushes = new ArrayList<>();

        messageQueue.sendMessage(new PaintBrushMessage(context.lookup(Route.class)), new MessageCallback() {
            @Override
            public void onComplete(final MessageResult aMessageResult) {
                if (!(aMessageResult.getResult() instanceof PaintBrush)) {
                    return;
                }

                paintBrushes.add((PaintBrush) aMessageResult.getResult());
            }
        });

        if (paintBrushes.isEmpty()) {
            return EmptyPaintBrush.INSTANCE;
        }

        final PaintBrush paintBrush = paintNamedWithPainterMethod(aNamed, paintBrushes.iterator().next());
        if (paintBrush.hasPainted()) {
            return paintBrush;
        }

        return EmptyPaintBrush.INSTANCE;
    }

    private PaintBrush paintNamedWithPainterMethod(final Object aNamed, final PaintBrush aPaintBrush) {
        final PaintContext painterContext = new PaintContextImpl(context.lookupAll(Bytecode
            .findClassesByInterfaceClass(Painter.class)), context, aNamed, aPaintBrush);
        final PaintBrush paintBrush = painterContext.forwardPaint();
        if (paintBrush == null) {
            return EmptyPaintBrush.INSTANCE;
        }

        paintBrush.closeBrush();
        return paintBrush;
    }

    private static final class EmptyPaintBrush implements PaintBrush {
        public static final PaintBrush INSTANCE = new EmptyPaintBrush();

        private EmptyPaintBrush() {
        }

        @Override
        public boolean hasPainted() {
            return false;
        }

        @Override
        public void paintBrush(final PaintBrush aPaintBrush) {
        }

        @Override
        public void paintComponent(final Object aComponent) {
        }

        @Override
        public void closeBrush() {
        }
    }
}
